package org.test4j.tutorial.test;

import lombok.Data;
import lombok.experimental.Accessors;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.test4j.mock.Invocation;
import org.test4j.mock.Mock;
import org.test4j.mock.MockUp;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class TargetObjectMockDemo {

    @DisplayName("对指定实例进行mock演示")
    @Test
    void test1() {
        /** 有一个配偶, 2个小孩 **/
        User user = new User("tom")
            .setSpouse(new User("mary"))
            .addChild(new User("mike"))
            .addChild(new User("jack"));
        String hi = user.sayHi();
        /** mock前的自我介绍 **/
        assertEquals("I'm tom and my spouse is mary.I have 2 children, mike and jack.", hi);
        /**
         * 对配偶进行mock
         */
        new MockUp(User.class, user.getSpouse()) {
            @Mock
            String getName(Invocation inv) {
                return "virtual " + inv.proceed();
            }
        };
        /**
         * 对小孩进行mock
         */
        new MockUp(User.class, user.getChildren()) {
            @Mock
            String getName(Invocation inv) {
                return "fictitious " + inv.proceed();
            }
        };
        hi = user.sayHi();
        /** mock后的自我介绍 **/
        assertEquals("I'm tom and my spouse is virtual mary.I have 2 children, fictitious mike and fictitious jack.", hi);
    }

    @DisplayName("对指定实例进行mock演示")
    @Test
    void test2() {
        /** 无配偶, 无小孩 **/
        User user = new User("tom");
        String hi = user.sayHi();
        /** mock前的自我介绍 **/
        assertEquals("I'm tom", hi);
        /**
         * 对配偶进行mock
         */
        new MockUp(User.class, user.getSpouse()) {
            @Mock
            String getName(Invocation inv) {
                return "virtual " + inv.proceed();
            }
        };
        /**
         * 对小孩进行mock
         */
        new MockUp(User.class, user.getChildren()) {
            @Mock
            String getName(Invocation inv) {
                return "fictitious " + inv.proceed();
            }
        };
        hi = user.sayHi();
        /** mock后的自我介绍 **/
        assertEquals("I'm tom", hi);
    }

    @Data
    @Accessors(chain = true)
    public static class User {
        private String name;

        private User spouse;

        private List<User> children = new ArrayList<>();

        public User(String name) {
            this.name = name;
        }

        public User addChild(User child) {
            children.add(child);
            return this;
        }

        /**
         * 自我介绍
         *
         * @return
         */
        public String sayHi() {
            StringBuilder buff = new StringBuilder();
            buff.append("I'm ").append(this.getName());
            if (this.spouse != null) {
                buff.append(" and my spouse is ").append(this.spouse.getName()).append(".");
            }
            if (!this.children.isEmpty()) {
                buff.append("I have ").append(children.size()).append(" children, ")
                    .append(this.children.stream().map(User::getName).collect(Collectors.joining(" and ")))
                    .append(".");
            }
            return buff.toString();
        }

        public String getName() {
            return name;
        }
    }
}
